package torres.map;

import java.util.ArrayList;
import torres.action.Effect;
import torres.entity.Avatar;
import torres.entity.Entity;
import torres.entity.NPC;
import torres.fileIO.ClassContainer;
import torres.fileIO.Saveable;
import torres.item.Item;
import torres.item.ObstacleItem;
import torres.item.TrapItem;
import torres.map.areaeffect.AreaEffect;
import torres.map.decal.Decal;
import torres.map.terrain.Terrain;

/**
 * @author Torres
 * The game world which contains list of maps on which the game can
 * be played, including the combat arena.
 */
public class World implements Saveable {
	
	private static final World instance = new World();
	private Avatar player;
	private Map currentMap;
	private Map combatArena;
	private ArrayList<Map> mapList;
	private ArrayList<Entity> enemyList;
	
	/**
	 * Constructs a World.
	 */
	private World() {
		
		this.currentMap = null;
		this.mapList = new ArrayList<Map>();
		this.enemyList = new ArrayList<Entity>();
	}

	/**
	 * @return the instance of the singleton
	 */
	public static World getInstance() {
		
		return instance;
	}
	
	/**
	 * @param map the map to be added to the world
	 */
	public void addMap(Map map) {
		
		if(!mapList.contains(map)) {
			
			mapList.add(map);
		}
	}
	
	/**
	 * @param mapName the name of the desired map
	 * @return the map having the same name as the supplied name
	 */
	public Map getMap(String mapName) {
		
		Map map = null;
		
		for(Map m: mapList) {
			
			String name = m.toString();
			if(name.equals(mapName)) {
				
				map = m;
				break;
			}
		}
		return map;
	}
	
	/**
	 * @return the current map within the world
	 */
	public Map getCurrentMap() {
		
		return currentMap;
	}
	
	/**
	 * @return a reference to the game player
	 */
	public Avatar getPlayer() {
		
		return player;
	}
	
	/**
	 * @return a list of the maps within the world
	 */
	public ArrayList<Map> getMapList() {
		
		return mapList;
	}
	
	/**
	 * @param map the map to become the current map within the world
	 */
	public void setCurrentMap(Map map) {
		
		if(!mapList.contains(map)) {
			
			mapList.add(map);
		}
		this.currentMap = map;
	}
	
	/**
	 * @param mapList a list of maps to add to the world
	 */
	public void setMapList(ArrayList<Map> mapList) {
		
		this.mapList = mapList;
	}
	
	/**
	 * @return the map of the combat arena
	 */
	public Map getCombatArena() {
		
		return combatArena;
	}
	
	/**
	 * @param combatArena the map which will become the combat arena
	 */
	public void setCombatArena(Map combatArena) {
		
		this.combatArena = combatArena;
	}
	
	/**
	 * @param player the avatar which will become the game player
	 */
	public void setPlayer(Avatar player) {
		
		this.player = player;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		
		return "World with Map: " + currentMap.toString();
	}
	
	//Stuff being forwarded to the current map
	
	public AreaEffect getAreaEffect(Location location) {
		
		return currentMap.getAreaEffect(location);
	}
	
	public Decal getDecal(Location location) {
		
		return currentMap.getDecal(location);
	}
	
	public Terrain getTerrain(Location location) {
		
		return currentMap.getTerrain(location);
	}
	
	public ArrayList<Entity> getEnemyList() {

		return enemyList;
	}

	public boolean hasAreaEffect(Location location) {
		
		return currentMap.hasAreaEffect(location);
	}
	
	public boolean hasDecal(Location location) {
		
		return currentMap.hasDecal(location);
	}
	
	public boolean hasEntity(Location location) {
		
		return currentMap.hasEntity(location);
	}
	
	public boolean hasItem(Location location) {
		
		return currentMap.hasItem(location);
	}
	
	public Entity getEntity(Location location) {
		
		return currentMap.getEntity(location);
	}
	
	public Item getItem(Location location) {
		
		return currentMap.getItem(location);
	}
	
	public void addEntity(Entity entity, Location location) {
		
		currentMap.addEntity(entity, location);
	}
	
	public void addItem(Item item, Location location) {
		currentMap.addItem(item, location);
	}
	
	public void removeEntity(Location location) {
		
		currentMap.removeEntity(location);
	}
	
	public void removeItem(Location location) {
		
		currentMap.removeItem(location);
	}
	
	public boolean isOccupied(Location location) {
		
		return hasEntity(location);
	}
	
	public boolean isValidLocation(Location location) {
		
		return currentMap.isValidLocation(location);
	}

	public boolean hasObstacle(Location location) {
		return currentMap.hasObstacle(location);
	}
	
	public boolean hasTrap(Location location) {
		return currentMap.hasTrap(location);
	}
	
	public ObstacleItem getObstacle(Location location) {
		return currentMap.getObstacle(location);
	}
	
	public TrapItem getTrap(Location location) {
		return currentMap.getTrap(location);
	}
	
	public void addObstacle(ObstacleItem obstacle, Location location) {
		currentMap.addObstacle(obstacle, location);
	}
	
	public void addTrap(TrapItem trap, Location location) {
		currentMap.addTrap(trap, location);
	}
	
	public void removeObstacle(Location location) {
		currentMap.removeObstacle(location);
	}
	
	public void removeTrap(Location location) {
		currentMap.removeTrap(location);
	}
		
	public boolean hasEffect(Location loc) {
		return currentMap.hasEffect(loc);
	}
	
	public Effect getEffect(Location loc) {
		return currentMap.getEffect(loc);
	}
	
	/* (non-Javadoc)
	 * @see torres.fileIO.Saveable#save()
	 */
	@Override
	public ClassContainer save() {
		ClassContainer curClass = new ClassContainer("World");
		ClassContainer curMap = new ClassContainer("CurrentMap");
		ClassContainer curMapList = new ClassContainer("MapList");
		ClassContainer curEnemyList = new ClassContainer("EnemyList");

		if(player!=null) curClass.addProperty(player.save());
		if(currentMap!=null) curMap.addProperty(currentMap.save());
		for(Map map: mapList) {
			if(map!=null) curMapList.addProperty(map.save());
		}
		for(Entity enemy: enemyList) {
			if(enemy!=null) curEnemyList.addProperty(enemy.save());
		}
		curClass.addProperty(curMap);
		curClass.addProperty(curMapList);
		curClass.addProperty(curEnemyList);
		
		return curClass;
	}

	/* (non-Javadoc)
	 * @see torres.fileIO.Saveable#load(torres.fileIO.ClassContainer)
	 */
	@Override
	public void load(ClassContainer property) {
		int i=0;
	//	Location dummyLocation = World.getInstance().getPlayer().getLocation();
		//currentMap.removeEntity(dummyLocation);
		player = new Avatar();
		player.load((ClassContainer)property.getProperty("Avatar"));
		ClassContainer currentMapCont = (ClassContainer)property.getProperty("CurrentMap");
		currentMapCont = (ClassContainer)currentMapCont.getProperty("Map");
		currentMap = new Map();
		currentMap.load(currentMapCont);
		currentMap.addEntity(player, player.getLocation());
		//currentMap.removeEntity(dummyLocation);
		ClassContainer mapListCont = (ClassContainer)property.getProperty("MapList");
		mapList.clear();
		ClassContainer mapCont;
		while((mapCont = (ClassContainer)(mapListCont.getProperty("Map", i)))!=null) {
			Map map = new Map();
			map.load(mapCont);
			mapList.add(map);
			i++;
		}
		i=0;
		ClassContainer enemyListCont = (ClassContainer)property.getProperty("EnemyList");
		ClassContainer enemyCont;
		enemyList.clear();
		while((enemyCont = (ClassContainer)enemyListCont.getProperty("NPC", i))!=null) {
			Entity enemy = new NPC();
			enemy.load(enemyCont);
			enemyList.add(enemy);
			i++;
		}	
	}
	
	public void killNPC(NPC npc) {
		currentMap.killNPC(npc);
	}
}
